/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdio>
#include <cstdlib>
#include <string>
#include <securec.h>
#include "js_native_api.h"
#include "js_native_api_types.h"
#include "napi/native_common.h"
#include "napi/native_api.h"
#include "cashdevice_def.h"
#include "cardsender_srv_client.h"
#include "hilog_wrapper.h"
#include "cardsender_utils.h"

using namespace SwanLink::Finance;
using namespace std;
using namespace OHOS;

thread_local static CardsenderSrvClient& g_cardsenderClient = CardsenderSrvClient::GetInstance();

/**
 * @brief 定义接口函数
 * static napi_value XXXX(napi_env env, napi_callback_info info)
 * @param env
 * @param info
 * @return napi_value
 */
static napi_value Open(napi_env env, napi_callback_info info)
{
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi Open Enter");
    napi_value this_arg = nullptr;
    void* priv_data = nullptr;
    size_t argc = 2;
    napi_value args[2] = { 0 };
    struct tTmpStruct {
        string name;
        Transport trans;
        int ret;
    };

    tTmpStruct* pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    int32_t argcForLog{argc};
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi open napi_get_cb_info argc is %{public}d", argcForLog);
    NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments");
    char tmpName[256] = { 0 };
    size_t valueLen = 0;
    NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], tmpName, 256, &valueLen));

    napi_value mode;
    napi_value dev;
    napi_value timeout;
    napi_value size;
    int iMode = -1;
    char cDev[MAX_DEV_NAME_LEN];
    int iTimeout = 0;
    int iSize = 0;
    size_t cDevSize = 0;
    NAPI_CALL(env, napi_get_named_property(env, args[1], "mode", &mode));
    NAPI_CALL(env, napi_get_named_property(env, args[1], "dev", &dev));
    NAPI_CALL(env, napi_get_named_property(env, args[1], "timeout", &timeout));
    NAPI_CALL(env, napi_get_named_property(env, args[1], "size", &size));
    NAPI_CALL(env, napi_get_value_int32(env, mode, &iMode));
    NAPI_CALL(env, napi_get_value_string_utf8(env, dev, cDev, MAX_DEV_NAME_LEN, &cDevSize));
    NAPI_CALL(env, napi_get_value_int32(env, timeout, &iTimeout));
    NAPI_CALL(env, napi_get_value_int32(env, size, &iSize));
    pVar->name = tmpName;
    pVar->trans.mode = (iMode == 1 ? AM_SERIAL : AM_USB);
    strcpy_s(pVar->trans.dev, MAX_DEV_NAME_LEN, cDev);
    pVar->trans.timeout = iTimeout;
    pVar->trans.size = iSize;
    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void* data)->napi_status {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        string& name = pVar->name;
        Transport& trans = pVar->trans;
        int& ret = pVar->ret;
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi open workfunc Enter,name %{public}s,trans.dev %{public}s",
            name.c_str(), trans.dev);
        ret = g_cardsenderClient.Open(name, trans);

        return napi_ok;
    };
    async_result_work result_func = [](napi_env env, void* data)->napi_value {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));
        return result;
    };
    async_free_work free_func = [](napi_env env, void* data) {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback* async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi open out");
    return AsyncPromise(env, "Open", async);
}

static napi_value Close(napi_env env, napi_callback_info info)
{
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi Close Enter");
    napi_value this_arg = nullptr;
    void* priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = { 0 };
    struct tTmpStruct {
        int handle;
        int ret;
    };

    tTmpStruct* pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    int32_t argcForLog{argc};
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi close napi_get_cb_info argc is %{public}d", argcForLog);
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void* data)->napi_status {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        int& handle = pVar->handle;
        int& ret = pVar->ret;
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi Close workfunc Enter");
        ret = g_cardsenderClient.Close(handle);

        return napi_ok;
    };
    async_result_work result_func = [](napi_env env, void* data)->napi_value {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));

        return result;
    };
    async_free_work free_func = [](napi_env env, void* data) {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback* async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi close out");
    return AsyncPromise(env, "Close", async);
}

static napi_value Reset(napi_env env, napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void* priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = { 0 };
    struct tTmpStruct {
        int handle;
        int ret;
    };

    tTmpStruct* pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    int32_t argcForLog{argc};
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi reset napi_get_cb_info argc is %{public}d", argcForLog);
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void* data)->napi_status {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        int& handle = pVar->handle;
        int& ret = pVar->ret;

        ret = g_cardsenderClient.Reset(handle);

        return napi_ok;
    };
    async_result_work result_func = [](napi_env env, void* data)->napi_value {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));

        return result;
    };
    async_free_work free_func = [](napi_env env, void* data) {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback* async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return AsyncPromise(env, "Reset", async);
}

static napi_value DispenseCard(napi_env env, napi_callback_info info)
{
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi DispenseCard Enter");
    napi_value this_arg = nullptr;
    void* priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = { 0 };
    struct tTmpStruct {
        int handle;
        int ret;
    };

    tTmpStruct* pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    int32_t argcForLog{argc};
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi dispensecard napi_get_cb_info argc is %{public}d", argcForLog);
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void* data)->napi_status {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        int& handle = pVar->handle;
        int& ret = pVar->ret;
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi DispenseCard work_func Enter");
        ret = g_cardsenderClient.DispenseCard(handle);
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi DispenseCard work_func out");
        return napi_ok;
    };
    async_result_work result_func = [](napi_env env, void* data)->napi_value {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi DispenseCard result Enter");
        return result;
    };
    async_free_work free_func = [](napi_env env, void* data) {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi DispenseCard free Enter");
        delete pVar;
    };
    AsyncCallback* async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi DispenseCard out");
    return AsyncPromise(env, "DispenseCard", async);
}

static napi_value GetSupportAccess(napi_env env, napi_callback_info info)
{
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi GetSupportAccess Enter");
    napi_value this_arg = nullptr;
    void* priv_data = nullptr;
    size_t argc = 0;
    vector<DevAccess>* pVar = new vector<DevAccess>;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, &this_arg, &priv_data));
    int32_t argcForLog{argc};
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi getsupportaccess napi_get_cb_info argc is %{public}d", argcForLog);
    NAPI_ASSERT(env, argc >= 0, "Wrong number of arguments");

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void* data)->napi_status {
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi GetSupportAccess work_func Enter");
        vector<DevAccess>* ret = (vector<DevAccess>*)data;
        *ret = g_cardsenderClient.GetSupportAccess();
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi GetSupportAccess work_func out , ret size %{public}d",
            (int)ret->size());
        return napi_ok;
    };
    async_result_work result_func = [](napi_env env, void* data)->napi_value {
        vector<DevAccess>* pDevAccessList = (vector<DevAccess>*)data;
        napi_value result;
        NAPI_CALL(env, napi_create_array(env, &result));
        napi_value devAccess;
        napi_value name;
        napi_value desc;
        napi_value transport;
        napi_value mode;
        napi_value dev;
        napi_value size;
        napi_value timeout;
        for (int i = 0; i < pDevAccessList->size(); ++i) {
            DevAccess& tmpDevAccess = pDevAccessList->at(i);
            NAPI_CALL(env, napi_create_object(env, &devAccess));
            NAPI_CALL(env, napi_create_object(env, &transport));
            NAPI_CALL(env, napi_create_int32(env, tmpDevAccess.transport.mode, &mode));
            NAPI_CALL(env, napi_create_int32(env, tmpDevAccess.transport.size, &size));
            NAPI_CALL(env, napi_create_int32(env, tmpDevAccess.transport.timeout, &timeout));
            NAPI_CALL(env, napi_create_string_utf8(env, tmpDevAccess.transport.dev, NAPI_AUTO_LENGTH, &dev));
            NAPI_CALL(env, napi_set_named_property(env, transport, "mode", mode));
            NAPI_CALL(env, napi_set_named_property(env, transport, "size", size));
            NAPI_CALL(env, napi_set_named_property(env, transport, "timeout", timeout));
            NAPI_CALL(env, napi_set_named_property(env, transport, "dev", dev));
            NAPI_CALL(env, napi_create_string_utf8(env, tmpDevAccess.name, NAPI_AUTO_LENGTH, &name));
            NAPI_CALL(env, napi_create_string_utf8(env, tmpDevAccess.desc, NAPI_AUTO_LENGTH, &desc));
            NAPI_CALL(env, napi_set_named_property(env, devAccess, "name", name));
            NAPI_CALL(env, napi_set_named_property(env, devAccess, "desc", desc));
            NAPI_CALL(env, napi_set_named_property(env, devAccess, "transport", transport));
            NAPI_CALL(env, napi_set_element(env, result, i, devAccess));
        }
        return result;
    };
    async_free_work free_func = [](napi_env env, void* data) {
        vector<DevAccess>* pVar = (vector<DevAccess>*)data;
        delete pVar;
    };
    AsyncCallback* async = new AsyncCallback(env, argc, nullptr, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi GetSupportAccess out");
    return AsyncPromise(env, "GetSupportAccess", async);
}

static napi_value QueryStatus(napi_env env, napi_callback_info info)
{
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus Enter");
    napi_value this_arg = nullptr;
    void* priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = { 0 };
    struct tTmpStruct {
        int handle;
        int suppBinStat;
        int retainBinStat;
        int ret;
    };

    tTmpStruct* pVar = new tTmpStruct();
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc,
    args, &this_arg, &priv_data));
    int32_t argcForLog{argc};
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi querystatus napi_get_cb_info argc is %{public}d", argcForLog);
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void* data)->napi_status {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        int& handle = pVar->handle;
        int& ret = pVar->ret;
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus work_func Enter");
        ret = g_cardsenderClient.QueryStatus(handle, &pVar->suppBinStat, &pVar->retainBinStat);
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI,
            "napi QueryStatus work_func ret %{public}d,suppBinStat %{public}d,retainBinStat %{public}d",
            ret, pVar->suppBinStat, pVar->retainBinStat);
        return napi_ok;
    };
    async_result_work result_func = [](napi_env env, void* data)->napi_value {
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus result_func Enter 1");
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value obj;
        napi_value napiRet;
        napi_value napiSuppBinStat;
        napi_value napiRetainBinStat;
        NAPI_CALL(env, napi_create_object(env, &obj));
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &napiRet));
        NAPI_CALL(env, napi_create_int32(env, pVar->suppBinStat, &napiSuppBinStat));
        NAPI_CALL(env, napi_create_int32(env, pVar->retainBinStat, &napiRetainBinStat));
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "ret %{public}d ,suppBinStat %{public}d retainBinStat %{public}d",
            pVar->ret, pVar->suppBinStat, pVar->retainBinStat);
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus result_func Enter 3");
        NAPI_CALL(env, napi_set_named_property(env, obj, "ret", napiRet));
        NAPI_CALL(env, napi_set_named_property(env, obj, "SuppBinStat", napiSuppBinStat));
        NAPI_CALL(env, napi_set_named_property(env, obj, "RetainBinStat", napiRetainBinStat));
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus result_func Enter 4");
        return obj;
    };
    async_free_work free_func = [](napi_env env, void* data) {
        tTmpStruct* pVar = reinterpret_cast<tTmpStruct*>(data);
        CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus free_func Enter ");
        delete pVar;
    };
    AsyncCallback* async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "napi QueryStatus out");
    return AsyncPromise(env, "QueryStatus", async);
}

/**
 * @brief 定义枚举类型
 */
static napi_value EnumClassConstructor(napi_env env, napi_callback_info info)
{
    napi_value thisArg = nullptr;
    void* data = nullptr;

    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);

    napi_value global = nullptr;
    napi_get_global(env, &global);

    return thisArg;
}

static napi_value CreateEnumACCESS_MODE(napi_env env, napi_value exports)
{
    napi_value values[2] = { 0 };
    napi_create_int32(env, (int32_t)AM_USB, &values[0]);
    napi_create_int32(env, (int32_t)AM_SERIAL, &values[1]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("AM_USB", values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("AM_SERIAL", values[1]),
    };
    napi_value result = nullptr;
    napi_define_class(env, "ACCESS_MODE", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr, 2, desc, &result);
    napi_set_named_property(env, exports, "ACCESS_MODE", result);
    return exports;
}

EXTERN_C_START
/*
 * function for module exports
 */
static napi_value CardsenderInit(napi_env env, napi_value exports)
{
    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "cardsender napi Enter");

    napi_property_descriptor desc[] = {
    DECLARE_NAPI_FUNCTION("Open", Open),
    DECLARE_NAPI_FUNCTION("Close", Close),
    DECLARE_NAPI_FUNCTION("Reset", Reset),
    DECLARE_NAPI_FUNCTION("DispenseCard", DispenseCard),
    DECLARE_NAPI_FUNCTION("GetSupportAccess", GetSupportAccess),
    DECLARE_NAPI_FUNCTION("QueryStatus", QueryStatus),
    DECLARE_CONST_VAR(ERR_POWER_DOWN),
    DECLARE_CONST_VAR(ERR_NO_CARD),
    DECLARE_CONST_VAR(ERR_SUPPLY_BIN_EMPTY),
    DECLARE_CONST_VAR(ERR_NO_SUPPLY_BIN),
    DECLARE_CONST_VAR(ERR_HARDWARE_ERROR),
    DECLARE_CONST_VAR(ERR_NO_RETAIN_BIN),
    DECLARE_CONST_VAR(ERR_SUPPLY_BIN_EMPTY),
    DECLARE_CONST_VAR(ERR_RETAIN_BIN_FULL),
    DECLARE_CONST_VAR(SUCCESS),
    DECLARE_CONST_VAR(ERROR),
    DECLARE_CONST_VAR(CANCEL),
    DECLARE_CONST_VAR(TIMEOUT),
    DECLARE_CONST_VAR(HANDLE_INVALID),
    DECLARE_CONST_VAR(ERR_DEVICE_NOT_OPEN),
    DECLARE_CONST_VAR(ERR_DEVICE_NOT_SUPPORT),
    DECLARE_CONST_VAR(ERR_DEVICE_OPEN_ALREADY),
    DECLARE_CONST_VAR(ERR_DEVICE_BUSY),
    DECLARE_CONST_VAR(ERR_DEVICE_NOT_EXIST),
    DECLARE_CONST_VAR(ERR_PLUGIN_NOT_EXIST),
    DECLARE_CONST_VAR(ERR_PLUGIN_LOAD_FAILED),
    DECLARE_CONST_VAR(ERR_TRANS_PORT_TACKUP),
    DECLARE_CONST_VAR(ERR_TRANS_PARAM_ERROR),
    DECLARE_CONST_VAR(ERR_NAPI_PARAM_TYPE_ERROR),
    DECLARE_CONST_VAR(ERR_NAPI_PARAM_NUMBER_WRONG),
    DECLARE_CONST_VAR(ERR_NAPI_PARAM_INVALID),
    DECLARE_CONST_VAR(ERR_TRANS_SEND_FAILED),
    DECLARE_CONST_VAR(ERR_TRANS_SEND_UNFINISHED),
    DECLARE_CONST_VAR(ERR_TRANS_RECV_FAILED),
    DECLARE_CONST_VAR(ERR_TRANS_RECV_UNFINISHED),
    DECLARE_CONST_VAR(ERR_CONFIG_LOAD_FAILED),
    DECLARE_CONST_VAR(ERR_CONFIG_NOT_FIND),
    DECLARE_CONST_VAR(ERR_DEVICE_OPEN_FAILED),
    DECLARE_CONST_VAR(ERR_DEVICE_PERMISSION_DENY),
    DECLARE_CONST_VAR(ERR_DEVICE_NO_DEVBUS),
    DECLARE_CONST_VAR(ERR_DEVICE_DRIVER_ERROR),
    DECLARE_CONST_VAR(ERR_DEVICE_DISCONNECT)
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));

    CreateEnumACCESS_MODE(env, exports);

    CARDSENDER_HILOGD(CARDSENDER_JS_NAPI, "cardsender napi Enter Success");

    return exports;
}
EXTERN_C_END

/*
 * Module definition
 */
static napi_module g_module = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = "cardsender",
    .nm_register_func = CardsenderInit,
    .nm_modname = "cardsender",
    .nm_priv = (reinterpret_cast<void*>(0)),
    .reserved = {0}
};

/*
 * Module registration
 */
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&g_module);
}
